package com.yqya.springboot.distributedlock.mysql.biz.impl;

import java.util.Calendar;
import java.util.Date;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yqya.springboot.distributedlock.mysql.biz.LockInfoBizService;
import com.yqya.springboot.distributedlock.mysql.constants.LockInfoConstants;
import com.yqya.springboot.distributedlock.mysql.domain.LockInfo;
import com.yqya.springboot.distributedlock.mysql.service.LockInfoService;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 锁信息业务服务接口实现类
 *
 * @author Kevin
 * @date 2019-08-03
 */
@Service
public class LockInfoBizServiceImpl implements LockInfoBizService {

    /**
     * 锁的过期时间（单位：秒），默认3s
     */
    private static final Integer DEFAULT_EXPIRED_SECONDS = 3;

    @Autowired
    private LockInfoService lockInfoService;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public boolean tryLock(String tag, Integer expiredSeconds) {
        if (StrUtil.isEmpty(tag)) {
            throw new NullPointerException();
        }

        Integer flag = LockInfoConstants.FlagEnum.NO.getValue();
        Integer status = LockInfoConstants.LockStatusEnum.YES.getValue();

        LockInfo lock = lockInfoService.findByTagWithLock(tag, flag);
        if (Objects.isNull(lock)) {
            LockInfo lockInfo = new LockInfo();
            lockInfo.setTag(tag);
            lockInfo.setFlag(flag);
            lockInfo.setStatus(status);
            Date nowDate = DateUtil.date();
            Date expirationTime = addSeconds(nowDate, expiredSeconds);
            lockInfo.setCreateTime(nowDate);
            lockInfo.setExpirationTime(expirationTime);
            lockInfoService.save(lockInfo);
            return true;
        } else {
            Date expirationTime = lock.getExpirationTime();
            Date nowDate = DateUtil.date();
            if (expirationTime.before(nowDate)) {
                expirationTime = addSeconds(nowDate, expiredSeconds);
                lock.setExpirationTime(expirationTime);
                lock.setUpdateTime(nowDate);
                LambdaUpdateWrapper<LockInfo> wrapper = Wrappers.lambdaUpdate();
                wrapper.eq(LockInfo::getTag, tag);
                lockInfoService.update(lock, wrapper);
                return true;
            }
        }

        return false;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void unlock(String tag) {
        if (StrUtil.isEmpty(tag)) {
            throw new NullPointerException();
        }

        Integer flag = LockInfoConstants.FlagEnum.NO.getValue();
        LockInfo lock = lockInfoService.findByTagWithLock(tag, flag);
        if (Objects.isNull(lock)) {
            Date nowDate = DateUtil.date();
            lock.setUpdateTime(nowDate);
            // 逻辑删除
            lock.setFlag(LockInfoConstants.FlagEnum.YES.getValue());
            lock.setStatus(LockInfoConstants.LockStatusEnum.NO.getValue());
            LambdaUpdateWrapper<LockInfo> wrapper = Wrappers.lambdaUpdate();
            wrapper.eq(LockInfo::getTag, tag);
            lockInfoService.update(lock, wrapper);
        }
    }

    private Date addSeconds(Date date, Integer seconds) {
        if (Objects.isNull(seconds)) {
            seconds = DEFAULT_EXPIRED_SECONDS;
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.SECOND, seconds);
        return calendar.getTime();
    }

}
